
# 现实生活中我们需要使用很多数据，而想要大量数据的增删改查大，以及数据的共享问题，就需要引入数据库（工具）
# 而我们主要学习的是SQLite数据库

# SQLite数据库是一个轻量级的数据库，它是一种关系型的管理系统
# 注意：SQLite3数据库同一时间只能时一个用户，单线程。（没办法做并发处理）

# SQLite数据库并不需要我们进行安装，因为python中自带

# 软件下载途径：https://sqlitebrowser.org/dl/ 或者 （后面不好用）https://github.com/sqlitebrowser/sqlitebrowser/releases
# 下载安装之后，就可以开始使用了

# 打开SQLite数据库软件
# 新建数据库（选择数据库的存放路径）
# 之后会出现数据表的命名
# 字段的命名 （说明：字段就是表的列名，字段的设置有：名称、（字段）类型、是否非空、主键、自增
#              补充说明：字段的类型有--interger：整数、text：文本、blob：大对象、real：数字、numeric：浮点型
# 在设置完表格字段之后，就可以点击确认生成表格（注意：我们是在上面进行的图形化操作，创建的表格，对应下面会生成sql的相关语句）
# 点击生成之后，会在桌面（也就是数据库的存放路径）生成一个db文件

# 建成表格之后，怎么插入数据呢
# 找到对应的字段并选中 --> 点击‘浏览数据’ --> 点击'>>'符号内的‘新建记录’


# 现在数据库有了，而我们的目的是想要通过python和SQLite进行数据的交互传输（数据的读取、存储、修改）
# 另外我们可以将生成的数据表直接复制到python当中的相应项目下（也就解决了路径问题）

##########################################################################
# 接下来，我们就需要写一个测试文件，大致实现python与SQLITE的连接
# 1.首先需要导入sqlite3模块
# import sqlite3
#
# # 2.数据库文件  （生成一个数据库对象）
# db_file = 'scores.db'
#
# # 3.获取与数据库的连接 （数据库本身也是一个软件，那么两个软件的连接也就需要一定的协议）
# conn = sqlite3.connect(db_file)    # 连接（scores.db）这个数据库
#
# # 4.编写sql语句
# sql = 'select * from score'      # 查询成绩表score的所有数据  注意：这里是要查询数据库当中的数据表
#
# # 5.执行sql语句 (需要借助游标cursor工具)
# cur = conn.cursor()    # 创建游标cursor
# cur.execute(sql)       # 利用游标执行sql语句
#
# # 6.打印结果
# context = cur.fetchall()  # 获取游标得到的所有数据
# print(context)            # 将数据打印出来
#
# # 7.关闭连接（一定要关闭连接！！！）
# cur.close()
# conn.close()
############################################################################

# 上面实现的python与SQLite的连接过程，接下来就要单独讲一下如何实现sqlite数据库的数据插入
# 补充说明:SQLite是一种嵌入式的数据库，它的数据库就是一个文件scores.db
#       由于SQLite是由c语言编写的（体积小），因此经常集成到各种应用程序中，甚至ios、android、Mac os、Liunx
#       python当中内置了sqlite数据库，所以我们可以直接使用
# 数据库：关系型数据库，一个数据库中会多张表，表和表之间通过主外键进行关联

# python当中操作sqlite数据库基本上是分为三步：
# 第一步：获取connection
# 第二步：连接之后需要打开游标，cursor，通过cursor执行sql语句来完成数据库的相关操作
# 第三步：最后一定要关闭连接，释放资源

# 这里我们将学习如何使用python直接对sqlite当中的数据进行增删改查
# 首先我们还是需要导入需要使用到的包
import sqlite3

# 创建数据库对象
db_file = 'scores.db'


# 将插入数据的操作封装到函数当中
def insert_score_data():
    # 1.获取连接
    conn = sqlite3.connect(db_file)
    # 2.打开游标curosr
    cur = conn.cursor()
    # 3.插入 的sql语句
    #   sql语句格式： insert into + 表名（列1， 列2， 列3....） values(值，值，值...)  注意：前面的列 和 后面的值 要一一对应
    sql = 'insert into score (stu_name, math_score, chinese_score) ' \
          'values(?,?,?)'        # 注意：value当中的数据是元组类型！！！
    data = ('钱六', 89.9, 78.2)
    # 执行sql语句
    cur.execute(sql, data)
    # 这里就实现插入数据('赵五', 89.9, 78.2)   本质上是：将插入的 values（）当中的问号依次替换成数据中对对应位置的值
    # 在执行插入时，需要进行显示提交数据
    conn.commit()       # 相当于是给出最终修改数据库的一个确认指令，否则数据库中的数据不会发生变化
    # 4.关闭连接（游标），释放资源
    cur.close()       # 关闭游标
    conn.close()      # 关闭连接

# 调用 添加数据操作
# insert_score_data()
# print('数据添加完毕！')
###################################################################
# 以上内容完成了如何连接python和数据库，同时还实现了通过python来增加数据库中的数据
# 接下里我们就要完成，如果通过python来实现对数据库当中的数据删除
# 注意：我们想要删除某项数据一定是要有一定的条件，否则就会导致删除不掉指定内容，或者是全部删除

# 删除数据
def delete_score_data():
    # 1.获取连接资源
    conn = sqlite3.connect(db_file)
    # 2.打开游标，进行sql语句
    cur = conn.cursor()
    # 删除的sql语句 删除语句的格式：delete from + 表名 where 列=？（条件）
    sql = 'delete from score where id=?'
    # 单独给出条件的值（注意也是元组类型）
    id = (3, )
    cur.execute(sql, id)
    # 进行提交
    conn.commit()
    # 4.关闭资源
    cur.close()
    conn.close()


# 调用删除数据操作
# delete_score_data()
# print('删除操作已完成！')
###########################################################
# 上面是通过python来删除了数据库中的数据
# 下面我们继续实现通过python来修改数据库中的数据信息

# 修改数据(修改数据也是需要基于某种条件的)
def update_score_data():
    # 1.获取连接
    conn = sqlite3.connect(db_file)
    # 2.打开游标，执行sql语句
    cur = conn.cursor()
    # 编写修改的sql语句 修改语句的格式： update 表名 set 列名= ?... where 条件
    sql = 'update score set math_score = ?,chinese_score= ? where id = 2'
    # 给出修改语句中需要用到的相关数据（还是用元组的形式）
    data = (98, 98, )     # 注意！！！元组中的数据，最后一个元素必须是 ', ' (逗号+空格)
    # 执行sql语句
    cur.execute(sql, data)
    # 3.提交数据
    conn.commit()
    # 4.关闭连接和游标，释放资源
    cur.close()
    conn.close()


# 调用修改操作
# update_score_data()
# print('修改操作已完成！')
#######################################################################
# 上面完成了数据的修改 ，接下来完成数据的查询（显示）

# 查询数据
def select_score_all():
    conn = sqlite3.connect(db_file)
    cur = conn.cursor()
    # 查询语句的格式： select 查找内容 from 表名 where 查找条件
    # 个人理解： 看谁（查找条件）的什么东西（查找内容）
    sql = 'select * from score where id = 1'  # *表示所有列
    cur.execute(sql)     # 执行查询的sql语句
    # 获取查询到的数据
    data = cur.fetchall()
    # 注意！！！这里的数据是按照：列表嵌套元组的形式返回的（整个表对应列表，每一条数据对应一个元组）
    print(data)

    # 注意：查询语句不需要提交commit
    cur.close()
    conn.close()


# 调用查询函数
# select_score_all()
###################################################################
# 上面我们完成了通过python来对数据库（SQLite）数据的添加、修改、和查询。下面我们要拓展一下如何添加多条数据

# 插入多条数据
# 首先构建以元组为每个元素的列表（也就是列表中嵌套着元组）
score_list = [('Jack', 80, 90), ('Bob', 70, 80), ('Rose', 60, 70), ('Sunny', 30, 40), ('Ada', 88, 99)]


def insert_mult_data():
    # 1.获取连接
    conn = sqlite3.connect(db_file)
    # 2.创建游标，写sql语句，执行命令
    cur = conn.cursor()   # 创建游标
    sql = 'insert into score(stu_name, math_score, chinese_score) values (?,?,?)'  # 写sql语句
    cur.executemany(sql, score_list)     # 对于多条数据的添加执行
    # 注意：多天数据添加的指令格式 : cur.executemany(sql语句, 构建好的多条数据)
    # 提交数据！！！
    conn.commit()
    # 3 关闭连接，释放资源
    cur.close()
    conn.close()
    return cur.rowcount     # 通过返回游标当中的行数，观察添加数据是否成功


# 测试
# print(insert_mult_data())
##############################################################
# 上面完成通过python向SQLite数据库插入多条数据
# 通过观察可以看到，添加、删除、修改、查询数据的操作之间含有相同操作（通用部分）
# 这样，我们就想到将通用的部分封装起来（封装到一个类当中），这样我们在需要的时候就可以对其进行调用
# 例如我们创建意见 db.py文件 然后将公共部分（连接、关闭资源做一个封装）

# 通用部分封装好之后，我们可以这样来使用
# 1.先导入写好的封装模块
from PyQt5基础系列课程.SQLite数据库.db import get_db_conn, close_db_conn


# 下面我们以查询所有数据为例
def get_data():
    conn = get_db_conn(db_file)
    cur = conn.cursor()
    sql = 'select * from score'
    cur.execute(sql)
    print(cur.fetchall())
    close_db_conn(cur, conn)


get_data()
##############################################################
# 上面我们就完成了功能模块的封装
# 其实封装我现在的理解就是将通用部分进行统一打包起来，然后别的地方使用时候，就直接调用
# 并且封装可以层层封装，通用的方法封装在函数中--> 把这些函数变成方法封装在类当中--> 把不同的类又可以封装在一个文件中（模块）
#                                      --> 把多个文件封装在一起（包）  （以上是个人理解）
